/**************************************************************************
Step 1. Include necessary header files such that the stuff your test logic needs is declared. 
Don't forget gtest.h, which declares the testing framework.                                                   
**************************************************************************/
#include "gtest/gtest.h"
#include "include/pf_cache.h"


/**************************************************************************
Step 2. Use test fixture to design your complex cases. 
You can add SetUp() to initialize the test data and 
TearDown() to clear the test data for common usage
Pls remember that when you using test fixture, need to use macro "TEST_F" instead of "TEST"                                                 
**************************************************************************/
class PfCacheTest : public testing::Test 
{
    /* This SetUp() will be called before every TEST CASE which is marked by macro "TEST_F" */
    virtual void SetUp() 
    {

    }

    /* This TearDown will be called after every TEST CASE which is marked by macro "TEST_F" */
    virtual void TearDown() 
    {

    }

};

#ifndef VALGRIND_CHECK /* Death test core dump makes valgrind too many errors */
class PfCacheDeathTest: public testing::Test 
{
    /* This SetUp() will be called before every TEST CASE which is marked by macro "TEST_F" */
    virtual void SetUp() 
    {

    }

    /* This TearDown will be called after every TEST CASE which is marked by macro "TEST_F" */
    virtual void TearDown() 
    {

    }

};
#endif /* #ifndef VALGRIND_CHECK */



struct pf_cache{
    /** Mutex to protect access to the structure */
    pthread_mutex_t mutex;
    /** Name of the cache objects in this cache (provided by the caller) */
    char *name;
    /** List of pointers to available buffers in this cache */
    void **ptr;
    /** The size of each element in this cache */
    size_t bufsize;
    /*preallocated object buffer begin*/
    char *buff_begin;
    /*preallocated object buffer end*/
    char *buff_end;
    /** max free elements in cache,0 meens no constaint */ 
    int max_free;
    /** The capacity of the list of elements */
    int freetotal;
    /** The current number of free elements */
    int freecurr;
    /** The constructor to be called each time we allocate more memory */
    pf_cache_constructor_f* constructor;
    /** The destructor to be called each time before we release memory */
    pf_cache_destructor_f* destructor;
};


typedef struct Test
{
    int32_t id;
    int32_t type;
    char* name;
}Test_t;


int normal_constructor(void* obj, void* notused1, int notused2)
{
    static int32_t id = 0;
    if(obj != NULL)
    {   
        Test_t *test = (Test_t*)obj;
        if(id >= 10)
            id = 0;
        test->id = id++;
        test->type = 2;
        test->name = (char*)malloc(sizeof("test"));
        if(test->name)
        {   
            strcpy(test->name,"test");
        }   
        else
        {   
            return -1; 
        }   

        notused1 = NULL;
        notused2 = 0;
        return 0;
    }   
    return -1; 
}

void normal_destructor(void* obj, void* notused)
{
    if(obj != NULL)
    {   
        Test_t *test = (Test_t*)obj;
        if(test->name)
        {   
            free(test->name); 
        }   
    }   
    notused = NULL;
}

int abnormal_constructor(void* obj, void* notused1, int notused2)
{
    static int i = 0;
    obj      = obj;
    notused1 = notused1;
    notused2 = notused2;
    if(i >= 10)
    {
        i = 0;
    }
    if(++i%3 == 0)
    {
        return -1;
    }
    return 0;
}

void abnormal_destructor(void* obj, void* notused)
{
    obj     = obj;
    notused = notused;
}


/***************************************************************************
  Step 3. Use macro "TEST_F" to start your test case                                      
  You can design your test case as the example
 ***************************************************************************/
TEST_F(PfCacheTest, pf_cache_create_test)
{

    /* initilize test data for this case */
    int i                               = 0;
    int j                               = 0;
    char *name                          = "Test";
    pf_cache_t* cache[10]               = {NULL};
    size_t bufsize                      = sizeof(Test_t);
    int initial_size                    = 5 ;
    int max_free                        = 5;
    size_t align                        = 1;
    pf_cache_constructor_f* constructor = NULL;
    pf_cache_destructor_f* destructor   = NULL;

    /* Call your function for test */

    cache[0] = pf_cache_create(name, bufsize, align, constructor, destructor,initial_size,max_free);
    EXPECT_NE((pf_cache_t*)NULL, cache[0]);

    max_free = 0;
    cache[1] = pf_cache_create(name, bufsize, align, constructor, destructor,initial_size,max_free);
    EXPECT_NE((pf_cache_t*)NULL, cache[0]);

    initial_size = 1024*1024*1024;
    cache[2]     = pf_cache_create(name, bufsize, align, constructor, destructor,initial_size,max_free);
    EXPECT_EQ((pf_cache_t*)NULL, cache[2]);

    pf_cache_destroy(cache[0]);
    pf_cache_destroy(cache[1]);
    pf_cache_destroy(cache[2]);
/*
    initial_size = 5;
    max_free     = 5;
    cache[3]     = pf_cache_create(name, bufsize, align, constructor, destructor,initial_size,max_free);
    EXPECT_EQ((pf_cache_t*)NULL, cache[3]);
*/

    constructor  = abnormal_constructor;
    destructor   = abnormal_destructor;
    initial_size = 1 ;
    max_free     = 5;
    for(i = 0;i<10;i++)
    {
        cache[i] = pf_cache_create(name, bufsize, align, constructor, destructor,initial_size,max_free);
        if(++j%3 != 0)
        {
            EXPECT_NE((pf_cache_t*)NULL, cache[i]);
        }
        else
        {
            EXPECT_EQ((pf_cache_t*)NULL, cache[i]);
        }
    }

    constructor  = NULL;
    destructor   = NULL;
    initial_size = 5 ;
    max_free     = 5;
    align        = 1;
    constructor  = NULL;
    destructor   = NULL;

#ifndef VALGRIND_CHECK /* Death test core dump makes valgrind too many errors */
    //death test
    name = NULL;
    EXPECT_DEATH(pf_cache_create(name, bufsize, align, constructor, destructor,initial_size,max_free),"");

    name    = "Test";
    bufsize = 0;
    EXPECT_DEATH(pf_cache_create(name, bufsize, align, constructor, destructor,initial_size,max_free),"");

    name         = "Test";
    bufsize      = sizeof(Test_t);
    initial_size = -1;
    EXPECT_DEATH(pf_cache_create(name, bufsize, align, constructor, destructor,initial_size,max_free),"");

    name     = "Test";
    bufsize  = sizeof(Test_t);
    max_free = -1;
    EXPECT_DEATH(pf_cache_create(name, bufsize, align, constructor, destructor,initial_size,max_free),"");

    name         = "Test";
    bufsize      = sizeof(Test_t);
    initial_size = 5;
    max_free     = 4;
    EXPECT_DEATH(pf_cache_create(name, bufsize, align, constructor, destructor,initial_size,max_free),"");
#endif /* #ifndef VALGRIND_CHECK */

    /* Clear the test data for this case */
    for(i = 0;i < 10;i++)
    {
        pf_cache_destroy(cache[i]);
    }
}


TEST_F(PfCacheTest, pf_cache_alloc_test)
{

    /* initilize test data for this case */
    int i                               = 0;
    int j                               = 0;
    Test_t *test[10];
    char *name                          = "Test";
    pf_cache_t* cache[10]               = {NULL};
    size_t bufsize                      = sizeof(Test_t);
    int initial_size                    = 5 ;
    int max_free                        = 5;
    size_t align                        = 1;
    pf_cache_constructor_f* constructor = normal_constructor;
    pf_cache_destructor_f* destructor   = normal_destructor;

    /* Call your function for test */

    cache[0] = pf_cache_create(name, bufsize, align, constructor, destructor,initial_size,max_free);
    EXPECT_NE((pf_cache_t*)NULL, cache[0]);
    for(i=0;i<10;i++)
    {
        test[i] = (Test_t *)pf_cache_alloc(cache[0]);
        EXPECT_NE((Test_t *)NULL, test[i]);
        if(i < initial_size)
        {
            EXPECT_EQ(4-i, test[i]->id)<<i<<" id:"<<test[i]->id;
        }
        else
        {
            EXPECT_EQ(i, test[i]->id)<<i<<" id:"<<test[i]->id;
        }
        EXPECT_EQ(2, test[i]->type)<<i<<" type:"<<test[i]->type;
        EXPECT_STREQ("test", test[i]->name)<<i<<" name:"<<test[i]->name;
    }
    EXPECT_EQ(0,cache[0]->freecurr);
    for(i=0;i<10;i++)
    {
        pf_cache_free(cache[0],test[i]); 
    }
    EXPECT_EQ(initial_size,cache[0]->freecurr);


    max_free = 0;
    cache[1] = pf_cache_create(name, bufsize, align, constructor, destructor,initial_size,max_free);
    EXPECT_NE((pf_cache_t*)NULL, cache[1]);

    for(i=0;i<10;i++)
    {
        test[i] = (Test_t *)pf_cache_alloc(cache[1]);
        EXPECT_NE((Test_t *)NULL, test[i]);
        if(i < initial_size)
        {
            EXPECT_EQ(4-i, test[i]->id)<<i<<" id:"<<test[i]->id;
        }
        else
        {
            EXPECT_EQ(i, test[i]->id)<<i<<" id:"<<test[i]->id;
        }
        EXPECT_EQ(2, test[i]->type);
        EXPECT_STREQ("test", test[i]->name);
    }
    EXPECT_EQ(0,cache[1]->freecurr);
    for(i=0;i<10;i++)
    {
        pf_cache_free(cache[1],test[i]); 
    }
    EXPECT_EQ(10,cache[1]->freecurr);


    constructor  = abnormal_constructor;
    destructor   = abnormal_destructor;
    initial_size = 1 ;
    max_free     = 1;
    cache[2]     = pf_cache_create(name, bufsize, align, constructor, destructor,initial_size,max_free);
    EXPECT_NE((pf_cache_t*)NULL, cache[2]);
    (Test_t *)pf_cache_alloc(cache[2]);

    j = 1;
    for(i=0;i<10;i++)
    {
        test[i] = (Test_t *)pf_cache_alloc(cache[2]);
        if(++j%3 != 0)
        {
            EXPECT_NE((Test_t *)NULL, test[i]);
        }
        else
        {
            EXPECT_EQ((Test_t *)NULL, test[i]);
        }
    }
    for(i=0;i<10;i++)
    {   
        if(test[i])
        {   
            pf_cache_free(cache[2],test[i]); 
        }   
    }  

#ifndef VALGRIND_CHECK
    //death test
    EXPECT_DEATH(pf_cache_alloc(NULL),"");
#endif /* #ifndef VALGRIND_CHECK */

    /* Clear the test data for this case */
    for(i = 0;i < 10;i++)
    {
        pf_cache_destroy(cache[i]);
    }
}


#ifndef VALGRIND_CHECK /* Death test core dump makes valgrind too many errors */
TEST_F(PfCacheDeathTest, pf_cache_free_dead_test)
{
    Test_t *test;
    char *name="Test";
    pf_cache_t* cache = NULL;
    size_t bufsize = sizeof(Test_t);
    int initial_size = 5 ;
    int max_free = 5;
    size_t align=1;
    cache = pf_cache_create(name, bufsize, align,NULL, NULL,initial_size,max_free);
    EXPECT_NE((pf_cache_t*)NULL, cache);
    test = (Test_t *)pf_cache_alloc(cache);
    EXPECT_NE((Test_t *)NULL, test);


    EXPECT_DEATH(pf_cache_free(NULL,test),"");
    EXPECT_DEATH(pf_cache_free(cache,NULL),"");


    pf_cache_free(cache,test); 
    pf_cache_destroy(cache);

}
#endif /* #ifndef VALGRIND_CHECK */

